use fudata::db;
use fudata::model::security::Market;
use fudata::model::todo::Todo;
use fudata::model::todo::TodoStatus;
use fudata::model::todo::TodoType;
use log::error;
use log::trace;
use std::sync::Arc;
use anyhow::Result;
use log::info;
use crossbeam_deque::Injector;

use crate::todo::crawl_etf_todo;
use crate::todo::crawl_kzz_todo;
use crate::todo::crawl_opf_todo;
use crate::todo::crawl_stock_todo;
use crate::todo::RECEIVE_TIMEOUT_MS;

use super::crawl_index_todo;
use super::MAX_WORKERS;

// Create a worker pool using Tokio
pub fn start_worker_pool(num_workers: u8) -> Arc<Injector<Todo>> {
    let injector = Arc::new(Injector::new());

    // let session_clone = Arc::clone(&session);

    // Spawn workers directly using tokio::spawn
    for worker_no in 0..num_workers {
        let ij_clone = Arc::clone(&injector);
        tokio::spawn(async move {
            loop {
                let task = ij_clone.steal().success();

                match task {
                    Some(todo) => {
                        // Process the todo using the handle_query function
                        let _sample = handle_crawl(worker_no, &todo).await;
                        // Do something with the sample...
                    }
                    None => {
                        trace!("(worker_crawl) Workder[{worker_no}] steal no todo, wait {}ms", RECEIVE_TIMEOUT_MS);
                        tokio::time::sleep(
                            std::time::Duration::from_millis(RECEIVE_TIMEOUT_MS)
                        ).await;
                    }
                }
            }
        });
    }

    injector
}

// Define the handle_query function
async fn handle_crawl(worker_no: u8, todo: &Todo) -> Result<()> {
    if let Some(type_) = &todo.type_ {
        match type_ {
            // securites
            TodoType::CrawlKzzList => {
                info!("Worker[{worker_no}] crawl_kzz_list:  {:?}", todo.id);
                let _ = crawl_kzz_todo::crawl_kzz_list(worker_no, todo).await;
            }
            TodoType::CrawlEtfList => {
                info!("Worker[{worker_no}] crawl_etf_list:  {:?}", todo.id);
                let _ = crawl_etf_todo::crawl_etf_list(worker_no, todo).await;
            }
            TodoType::CrawlOpfList => {
                info!("Worker[{worker_no}] crawl_opf_list:  {:?}", todo.id);
                let _ = crawl_opf_todo::crawl_opf_list(worker_no, todo).await;
            }
            TodoType::CrawlStockList => {
                // Process the todo
                info!("Worker[{worker_no}] crawl_stock_list: {:?}", todo.id);
                let _ = crawl_stock_todo::crawl_stock_list(worker_no, todo).await;
            }
            TodoType::CrawlIndexList => {
                // Process the todo
                info!("Worker[{worker_no}] crawl_index_list: {:?}", todo.id);
                let _ = crawl_index_todo::crawl_index_list(worker_no, todo).await;
            }
            // klines
            TodoType::CrawlKzzKlines => {
                info!("Worker[{worker_no}]  crawl_kzz_klines: {:?}", todo.id);
                let _ = crawl_kzz_todo::crawl_kzz_klines(worker_no, todo).await;
            }
            TodoType::CrawlEtfKlines => {
                info!("Worker[{worker_no}] crawl_etf_klines: {:?}", todo.id);
                let _ = crawl_etf_todo::crawl_etf_klines(worker_no, todo).await;
            }
            TodoType::CrawlOpfValues => {
                info!("Worker[{worker_no}] crawl_opf_values: {:?}", todo.id);
                let _ = crawl_opf_todo::crawl_opf_values(worker_no, todo).await;
            }
            TodoType::CrawlStockKlines => {
                // Process the todo
                info!("Worker[{worker_no}] crawl_stock_klines: {:?}", todo.id);
                let _ = crawl_stock_todo::crawl_stock_klines(worker_no, todo).await;
            }
            TodoType::CrawlIndexKlines => {
                // Process the todo
                info!("Worker[{worker_no}] crawl_index_klines: {:?}", todo.id);
                let _ = crawl_index_todo::crawl_index_klines(worker_no, todo).await;
            }
            _ => {
                error!("(handle_crawl) unknown todo.type_:{}", type_);
            }
        }
    }

    Ok(())
}

pub async fn generate_crawl_todos(max_days: i64) {
    if false {
        // download kzz
        let _ = crawl_kzz_todo::generate_todo_crawl_kzz_list().await;
        let _ = crawl_kzz_todo::generate_todos_crawl_kzz_klines(max_days).await;
        // download etf
        let _ = crawl_etf_todo::generate_todo_crawl_etf_list().await;
        let _ = crawl_etf_todo::generate_todos_crawl_etf_klines().await;
        // download opf
        let _ = crawl_opf_todo::generate_todo_crawl_opf_list().await;
        let _ = crawl_opf_todo::generate_todos_crawl_opf_values().await;
        // download stock
        let _ = crawl_stock_todo::generate_todo_crawl_stock_list().await;
        let _ = crawl_stock_todo::generate_todos_crawl_stock_klines(Some(Market::SH)).await;
        let _ = crawl_stock_todo::generate_todos_crawl_stock_klines(Some(Market::SZ)).await;
        // download indexes
        let _ = crawl_index_todo::generate_todo_crawl_index_list().await;
        let _ = crawl_index_todo::generate_todos_crawl_index_klines(max_days).await;
    } else {
        // download etf
        let _ = crawl_etf_todo::generate_todo_crawl_etf_list().await;
        let _ = crawl_etf_todo::generate_todos_crawl_etf_klines().await;

        // download stock
        let _ = crawl_stock_todo::generate_todo_crawl_stock_list().await;
        let _ = crawl_stock_todo::generate_todos_crawl_stock_klines(Some(Market::SH)).await;
        let _ = crawl_stock_todo::generate_todos_crawl_stock_klines(Some(Market::SZ)).await;
    }
}

pub async fn get_oldest_crawl_todos() -> Result<Vec<Todo>> {
    let type_ = Some(
        vec![
            TodoType::CrawlKzzKlines,
            TodoType::CrawlKzzList,
            TodoType::CrawlEtfList,
            TodoType::CrawlEtfKlines,
            TodoType::CrawlStockList,
            TodoType::CrawlStockKlines,
            TodoType::CrawlIndexList,
            TodoType::CrawlIndexKlines
        ]
    );
    let status = Some(TodoStatus::Pending);
    // Select todos and push them onto the queue if not already enqueued
    db::todo::select(
        &type_,
        // &Some(TodoType::UpdateEtfList),
        // &Some(TodoType::UpdateEtfKlines),
        &status,
        true,
        0,
        (MAX_WORKERS as u16) * 1000
    ).await
}
